<!--
 * @Description: 文件管理
 * @Author: huguodong
 * @Date: 2023-12-15 15:45:10
!-->
<template>
  <div class="file-manager">
    <!-- 统计卡片 -->
    <el-row :gutter="10" class="mb-2">
      <el-col :span="6">
        <el-card shadow="hover">
          <div class="stat-card">
            <div class="stat-icon files">
              <el-icon :size="40">
                <Folder />
              </el-icon>
            </div>
            <div class="stat-info">
              <div class="stat-value">{{ statistics.totalFiles }}</div>
              <div class="stat-label">总文件数</div>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="6">
        <el-card shadow="hover">
          <div class="stat-card">
            <div class="stat-icon size">
              <el-icon :size="40">
                <DocumentCopy />
              </el-icon>
            </div>
            <div class="stat-info">
              <div class="stat-value">{{ formatFileSize(statistics.totalSize) }}</div>
              <div class="stat-label">总大小</div>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="6">
        <el-card shadow="hover">
          <div class="stat-card">
            <div class="stat-icon images">
              <el-icon :size="40">
                <Picture />
              </el-icon>
            </div>
            <div class="stat-info">
              <div class="stat-value">{{ statistics.imageCount }}</div>
              <div class="stat-label">图片文件</div>
            </div>
          </div>
        </el-card>
      </el-col>
      <el-col :span="6">
        <el-card shadow="hover">
          <div class="stat-card">
            <div class="stat-icon documents">
              <el-icon :size="40">
                <Document />
              </el-icon>
            </div>
            <div class="stat-info">
              <div class="stat-value">{{ statistics.documentCount }}</div>
              <div class="stat-label">文档文件</div>
            </div>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <!-- 文件列表 -->
    <div class="table-box">
      <ProTable ref="proTable" :columns="columns" :init-param="initParam" :request-api="fileApi.page" title="文件列表">
        <!-- 表格 header 按钮 -->
        <template #tableHeader="scope">
          <el-space wrap>
            <el-select v-model="initParam.fileType" clearable placeholder="文件类型" style="width: 150px" @change="refreshTable">
              <el-option label="图片" value="image" />
              <el-option label="文档" value="document" />
              <el-option label="视频" value="video" />
              <el-option label="音频" value="audio" />
              <el-option label="压缩包" value="archive" />
              <el-option label="其他" value="other" />
            </el-select>
            <el-input v-model="initParam.searchKey" clearable placeholder="请输入文件名称" style="width: 250px" @clear="refreshTable">
              <template #append>
                <el-button :icon="Search" @click="refreshTable" />
              </template>
            </el-input>
            <s-button type="primary" @click="onUpload">
              <el-icon>
                <Upload />
              </el-icon>
              上传文件
            </s-button>
            <s-button :disabled="!scope.isSelected" plain type="danger" @click="onDelete(scope.selectedListIds, '删除所选文件')">
              <el-icon>
                <Delete />
              </el-icon>
              批量删除
            </s-button>
          </el-space>
        </template>

        <!-- 文件名称 -->
        <template #fileName="scope">
          <div class="file-name">
            <el-icon :size="20" :style="{ color: getFileIconColor(scope.row.suffix || '') }" class="file-icon">
              <component :is="getFileIcon(scope.row.suffix || '')" />
            </el-icon>
            <span class="name-text">{{ scope.row.name }}</span>
          </div>
        </template>

        <!-- 文件大小 -->
        <template #fileSize="scope">
          <el-tag size="small" type="info">{{ formatFileSize(scope.row.sizeKb * 1024) }}</el-tag>
        </template>

        <!-- 文件类型 -->
        <template #fileType="scope">
          <el-tag :type="getFileTypeTag(getFileTypeFromSuffix(scope.row.suffix || ''))" size="small">
            {{ getFileTypeFromSuffix(scope.row.suffix || "") }}
          </el-tag>
        </template>
        <!-- 缩略图 -->
        <template #thumbnail="scope">
          <!-- 如果有缩略图且是图片类型,显示缩略图 -->
          <el-image
            v-if="scope.row.thumbnail"
            :preview-src-list="[scope.row.thumbnail]"
            :src="scope.row.thumbnail"
            fit="cover"
            style="width: 50px; height: 50px; border-radius: 4px; cursor: pointer"
          />
          <!-- 否则显示文件类型图标 -->
          <el-icon v-else :color="getFileIconColor(scope.row.suffix || '')" :size="40">
            <component :is="getFileIcon(scope.row.suffix || '')" />
          </el-icon>
        </template>

        <!-- 操作 -->
        <template #operation="scope">
          <el-space>
            <s-button :opt="FormOptEnum.VIEW" link @click="onPreview(scope.row)">预览</s-button>
            <s-button link type="primary" @click="onDownload(scope.row)">
              <el-icon>
                <Download />
              </el-icon>
              下载
            </s-button>
            <s-button :opt="FormOptEnum.DELETE" link type="danger" @click="onDelete([scope.row.id], `删除【${scope.row.name}】`)"> 删除 </s-button>
          </el-space>
        </template>
      </ProTable>

      <!-- 上传组件 -->
      <UploadComp ref="uploadRef" />
      <!-- 预览组件 -->
      <Preview ref="previewRef" />
    </div>
  </div>
</template>

<script lang="ts" name="devFile" setup>
import { reactive, ref } from "vue";
import { ElMessage } from "element-plus";
import { Delete, Document, DocumentCopy, Download, Folder, Headset, Picture, Search, Upload, VideoCamera } from "@element-plus/icons-vue";
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
import { fileApi } from "@/api";
import { FormOptEnum } from "@/enums";
import { useHandleData } from "@/hooks/useHandleData";
import { useUserStore } from "@/stores/modules";
import UploadComp from "./components/upload.vue";
import Preview from "./components/preview.vue";

const userStore = useUserStore();

// 表格初始化参数
interface InitParam {
  searchKey?: string;
  fileType?: string;
}

const initParam = reactive<InitParam>({});

// 获取 ProTable 元素
const proTable = ref<ProTableInstance>();
const uploadRef = ref<InstanceType<typeof UploadComp> | null>(null);
const previewRef = ref<InstanceType<typeof Preview> | null>(null);

// 统计数据
const statistics = reactive({
  totalFiles: 0,
  totalSize: 0,
  imageCount: 0,
  documentCount: 0
});

// 表格配置项
const columns: ColumnProps<any>[] = [
  { type: "selection", fixed: "left", width: 50 },
  { prop: "thumbnail", label: "缩略图", width: 80 },
  { prop: "fileName", label: "文件名称", minWidth: 250 },
  { prop: "fileSize", label: "文件大小", width: 120 },
  { prop: "fileType", label: "文件类型", width: 120 },
  { prop: "createUserName", label: "上传者", width: 120 },
  { prop: "createTime", label: "上传时间", width: 180 },
  { prop: "operation", label: "操作", width: 220, fixed: "right" }
];

/**
 * 打开上传对话框
 */
function onUpload() {
  uploadRef.value?.onOpen(refreshTable);
}

/**
 * 预览文件
 */
function onPreview(record: any) {
  previewRef.value?.onOpen(record);
}

/**
 * 下载文件
 */
async function onDownload(record: any) {
  try {
    const { accessToken } = userStore;
    if (!accessToken) {
      ElMessage.error("未登录,请先登录");
      return;
    }

    const response = await fetch(`/api/sys/dev/file/download?id=${record.id}`, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${accessToken}`
      }
    });

    if (!response.ok) {
      ElMessage.error("下载失败");
      return;
    }

    // 获取文件blob
    const blob = await response.blob();
    // 创建下载链接
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = record.name || "download";
    document.body.appendChild(a);
    a.click();
    // 清理
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
  } catch (error) {
    ElMessage.error("下载失败");
  }
}

/**
 * 删除文件
 */
async function onDelete(ids: (number | string)[], msg: string) {
  await useHandleData(fileApi.delete, { ids }, msg);
  refreshTable();
}

/**
 * 刷新表格
 */
function refreshTable() {
  proTable.value?.refresh();
  updateStatistics();
}

/**
 * 更新统计数据
 */
async function updateStatistics() {
  try {
    const res = await fileApi.statistics();
    Object.assign(statistics, res.data);
  } catch (error) {
    console.error("获取统计数据失败", error);
  }
}

/**
 * 格式化文件大小
 */
function formatFileSize(bytes: number): string {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB", "TB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return (bytes / Math.pow(k, i)).toFixed(2) + " " + sizes[i];
}

/**
 * 获取文件图标
 */
function getFileIcon(ext: string) {
  if (!ext) return Document;
  const lowerExt = ext.toLowerCase();

  const imageExts = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"];
  const videoExts = ["mp4", "webm", "ogg", "avi", "mov"];
  const audioExts = ["mp3", "wav", "ogg", "flac"];

  if (imageExts.some(imgExt => lowerExt.includes(imgExt))) return Picture;
  if (videoExts.some(vidExt => lowerExt.includes(vidExt))) return VideoCamera;
  if (audioExts.some(audExt => lowerExt.includes(audExt))) return Headset;
  return Document;
}

/**
 * 获取文件图标颜色
 */
function getFileIconColor(ext: string): string {
  if (!ext) return "#909399";
  const lowerExt = ext.toLowerCase();

  const imageExts = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"];
  const videoExts = ["mp4", "webm", "ogg", "avi", "mov"];
  const audioExts = ["mp3", "wav", "ogg", "flac"];

  if (imageExts.some(imgExt => lowerExt.includes(imgExt))) return "#67C23A";
  if (videoExts.some(vidExt => lowerExt.includes(vidExt))) return "#E6A23C";
  if (audioExts.some(audExt => lowerExt.includes(audExt))) return "#409EFF";
  return "#909399";
}

/**
 * 根据文件后缀获取文件类型
 */
function getFileTypeFromSuffix(suffix: string): string {
  if (!suffix) return "other";

  suffix = suffix.toLowerCase();

  const imageExts = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "psd"];
  if (imageExts.some(ext => suffix.includes(ext))) return "image";

  const documentExts = ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf", "txt", "md"];
  if (documentExts.some(ext => suffix.includes(ext))) return "document";

  const videoExts = ["mp4", "avi", "mov", "wmv", "flv", "mkv", "webm"];
  if (videoExts.some(ext => suffix.includes(ext))) return "video";

  const audioExts = ["mp3", "wav", "flac", "ape", "ogg", "aac"];
  if (audioExts.some(ext => suffix.includes(ext))) return "audio";

  const archiveExts = ["zip", "rar", "7z", "tar", "gz", "bz2"];
  if (archiveExts.some(ext => suffix.includes(ext))) return "archive";

  return "other";
}

/**
 * 获取文件类型标签颜色
 */
function getFileTypeTag(type: string): "success" | "warning" | "info" | "primary" | "danger" {
  const typeMap: Record<string, "success" | "warning" | "info" | "primary" | "danger"> = {
    image: "success",
    video: "warning",
    audio: "primary",
    document: "info",
    archive: "info",
    other: "info"
  };
  return typeMap[type] || "info";
}

// 页面加载时更新统计数据
onMounted(() => {
  updateStatistics();
});
</script>

<style lang="scss" scoped>
.file-manager {
  .stat-card {
    display: flex;
    align-items: center;
    gap: 20px;

    .stat-icon {
      width: 70px;
      height: 70px;
      border-radius: 10px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;

      &.files {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      }

      &.size {
        background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      }

      &.images {
        background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
      }

      &.documents {
        background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
      }
    }

    .stat-info {
      flex: 1;

      .stat-value {
        font-size: 28px;
        font-weight: bold;
        color: #303133;
      }

      .stat-label {
        font-size: 14px;
        color: #909399;
        margin-top: 5px;
      }
    }
  }

  .file-name {
    display: flex;
    align-items: center;
    gap: 8px;

    .file-icon {
      flex-shrink: 0;
    }

    .name-text {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  :deep(.el-card__body) {
    padding: 15px;
  }
}
</style>
